Skip to content

Python: fix(core): restrict unpickler module-prefix allowlist to types only#5923

Open
White-Mouse wants to merge 2 commits into
microsoft:mainfrom
White-Mouse:fix/restricted-unpickler-non-type-globals
Open

Python: fix(core): restrict unpickler module-prefix allowlist to types only#5923
White-Mouse wants to merge 2 commits into
microsoft:mainfrom
White-Mouse:fix/restricted-unpickler-non-type-globals

Conversation

@White-Mouse

@White-Mouse White-Mouse commented May 18, 2026

Copy link
Copy Markdown
Contributor

Motivation & Context

The restricted checkpoint decoder allows framework and OpenAI SDK types by module prefix. The previous implementation returned any global from those modules, and the allowlisted builtins.getattr could also traverse attributes on an allowed type. Together, those behaviors allowed a crafted pickle to reach a non-type callable such as _RestrictedUnpickler.__init__.__globals__["pickle"].loads before the post-deserialization type check.

This change tightens that defense-in-depth path while preserving the checkpoint format and supported type reconstruction.

Description & Review Guide

  • What are the major changes? Package-prefix and caller-supplied allowlist entries must resolve to a type. During restricted unpickling, builtins.getattr is replaced with a resolver that only permits type-to-type attribute lookup. Regression tests cover non-type globals, legitimate nested-type lookup, and the full getattr/__globals__/inner-pickle.loads chain.
  • What is the impact of these changes? Valid built-in, framework, OpenAI SDK, explicitly allowed, and nested type reconstruction remains supported. Non-type globals and non-type attribute traversal are rejected with pickle.UnpicklingError.
  • What do you want reviewers to focus on? Please review the compatibility boundary of the restricted getattr replacement and whether the regression coverage captures the expected nested-type reconstruction cases.

Related Issue

No issue is linked yet. A maintainer requested an open issue, and this PR will be linked once one is available.

Contribution Checklist

  • The code builds clean without any errors or warnings
  • All unit tests pass, and I have added new tests where possible
  • The PR follows the Contribution Guidelines
  • This PR is linked to an issue and there is no other open PR for this issue (see Related Issue above).
  • This is not a breaking change. If it is a breaking change, add the breaking change label (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and title prefix in sync automatically.

Copilot AI review requested due to automatic review settings May 18, 2026 16:39
@moonbox3 moonbox3 added the python Usage: [Issues, PRs], Target: Python label May 18, 2026
@github-actions github-actions Bot changed the title fix(core): restrict unpickler module-prefix allowlist to types only Python: fix(core): restrict unpickler module-prefix allowlist to types only May 18, 2026
@White-Mouse

Copy link
Copy Markdown
Contributor Author

@microsoft-github-policy-service agree

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Tightens the Python checkpoint restricted unpickling boundary by preventing agent_framework.* and openai.types.* module-prefix allowlisting from returning non-type globals (e.g., functions/modules), mitigating gadget chains that can pivot via builtins.getattr.

Changes:

  • Updates _RestrictedUnpickler.find_class to resolve globals under allowed prefixes and permit them only if they are actual type objects.
  • Adds a dedicated UnpicklingError message for blocked non-type globals from allowed prefixes.

Comment thread python/packages/core/agent_framework/_workflows/_checkpoint_encoding.py Outdated
Comment thread python/packages/core/agent_framework/_workflows/_checkpoint_encoding.py Outdated
Comment thread python/packages/core/agent_framework/_workflows/_checkpoint_encoding.py Outdated
@White-Mouse White-Mouse force-pushed the fix/restricted-unpickler-non-type-globals branch from f514068 to 04d425a Compare June 2, 2026 16:52
@moonbox3

moonbox3 commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

@White-Mouse thanks for your contribution. Is there an open issue we can link to? Additionally, please help to fix the merge conflict.

@White-Mouse White-Mouse force-pushed the fix/restricted-unpickler-non-type-globals branch from 04d425a to b996da1 Compare July 10, 2026 05:17
@github-actions

Copy link
Copy Markdown
Contributor

Python Test Coverage

Python Test Coverage Report •
FileStmtsMissCoverMissing
packages/core/agent_framework/_workflows
   _checkpoint_encoding.py78198%119
TOTAL44132527188% 

Python Unit Test Overview

Tests Skipped Failures Errors Time
8843 33 💤 0 ❌ 0 🔥 2m 16s ⏱️

Comment thread python/packages/core/agent_framework/_workflows/_checkpoint_encoding.py Outdated
@White-Mouse White-Mouse force-pushed the fix/restricted-unpickler-non-type-globals branch from e255fd1 to 1bc4077 Compare July 14, 2026 12:56
if not isinstance(obj, type) or not isinstance(name, str):
raise pickle.UnpicklingError("Checkpoint deserialization blocked for unsafe attribute traversal.")

resolved = getattr(obj, name)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we preserve enum-by-name checkpoints here? enum.pickle_by_enum_name reconstructs a member as getattr(Color, "RED"), so this type-only check now rejects a caller-allowlisted Enum that the base code decodes for protocols 3-5, including protocol 5 written by encode_checkpoint_value. Could the wrapper narrowly accept a real member of an already permitted Enum class while keeping other non-type attributes blocked?

@White-Mouse White-Mouse force-pushed the fix/restricted-unpickler-non-type-globals branch from 1bc4077 to 9dd038e Compare July 15, 2026 04:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

python Usage: [Issues, PRs], Target: Python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants